<script setup lang="ts">
import { ref, computed, watch } from "vue";
import { open } from "@tauri-apps/plugin-dialog";
import { readTextFile } from "@tauri-apps/plugin-fs";
import TreeNode from "./components/TreeNode.vue";
import ConvertTool from "./components/ConvertTool.vue";
import JsonCompare from "./components/JsonCompare.vue";

// Tab数据接口
interface TabData {
  id: string;
  name: string;
  title: string;
  jsonInput: string;
  formattedJson: string;
  compressedJson: string;
  jsonString: string;
  jsonTree: any;
  errorMessage: string;
  convertedResult: string;
  convertedFormat: string;
  searchQuery: string;
  selectedNode: any;
  showDetails: boolean;
  activeViewTab: string; // 当前视图tab（格式化、压缩等）
}

// 响应式数据
const tabs = ref<TabData[]>([]);
const activeTabId = ref<string>("");
const tabCounter = ref(1);
const showEscapeDropdown = ref(false); // 转义下拉菜单显示状态
const showJsonCompare = ref(false); // JSON对比弹窗显示状态

// 计算当前活跃的tab数据
const currentTab = computed(() => {
  return tabs.value.find(tab => tab.id === activeTabId.value) || null;
});

// 创建新tab
function createNewTab(): TabData {
  const newTab: TabData = {
    id: `tab-${tabCounter.value}`,
    name: `JSON ${tabCounter.value}`,
    title: `JSON ${tabCounter.value}`,
    jsonInput: "",
    formattedJson: "",
    compressedJson: "",
    jsonString: "",
    jsonTree: null,
    errorMessage: "",
    convertedResult: "",
    convertedFormat: "",
    searchQuery: "",
    selectedNode: null,
    showDetails: false,
    activeViewTab: "formatted"
  };
  tabCounter.value++;
  return newTab;
}

// 创建新的tab数据
function createTabData(id: string): TabData {
  const tabNumber = tabCounter.value;
  return {
    id,
    name: `Tab ${id}`,
    title: `JSON ${tabNumber}`,
    jsonInput: "",
    formattedJson: "",
    compressedJson: "",
    jsonString: "",
    jsonTree: null,
    errorMessage: "",
    convertedResult: "",
    convertedFormat: "",
    searchQuery: "",
    selectedNode: null,
    showDetails: false,
    activeViewTab: "formatted"
  };
}

// 添加新tab
function addNewTab() {
  const newTab = createNewTab();
  tabs.value.push(newTab);
  activeTabId.value = newTab.id;
}

// 关闭tab
function closeTab(tabId: string) {
  const index = tabs.value.findIndex(tab => tab.id === tabId);
  if (index === -1) return;
  
  tabs.value.splice(index, 1);
  
  // 如果关闭的是当前活跃tab，切换到其他tab
  if (activeTabId.value === tabId) {
    if (tabs.value.length > 0) {
      // 优先选择前一个tab，如果没有则选择后一个
      const newIndex = index > 0 ? index - 1 : 0;
      activeTabId.value = tabs.value[newIndex].id;
    } else {
      activeTabId.value = "";
    }
  }
}

// 切换tab
function switchTab(tabId: string) {
  activeTabId.value = tabId;
}

// 初始化第一个tab
if (tabs.value.length === 0) {
  addNewTab();
}

// 计算属性：搜索高亮
const highlightedJson = computed(() => {
  if (!currentTab.value || !currentTab.value.searchQuery || !currentTab.value.formattedJson) {
    return currentTab.value?.formattedJson || "";
  }
  const regex = new RegExp(`(${escapeRegExp(currentTab.value.searchQuery)})`, 'gi');
  return currentTab.value.formattedJson.replace(regex, '<mark>$1</mark>');
});

// 搜索匹配的节点路径
const searchMatchedPaths = ref<Set<string>>(new Set());

// 获取节点显示文本（用于搜索）
function getNodeDisplayText(node: any): string {
  if (node.type === "object") {
    const childCount = node.children?.length || 0;
    return `${node.key} {${childCount} 个属性}`;
  } else if (node.type === "array") {
    return `${node.key} [${node.length} 个元素]`;
  } else {
    return `${node.key}: ${JSON.stringify(node.value)}`;
  }
}

// 搜索匹配算法
function searchInTree(node: any, query: string): boolean {
  if (!query.trim()) return false;
  
  const lowerQuery = query.toLowerCase();
  let hasMatch = false;
  
  // 检查当前节点是否匹配
  const nodeText = getNodeDisplayText(node).toLowerCase();
  const keyText = node.key?.toLowerCase() || '';
  const valueText = node.value ? String(node.value).toLowerCase() : '';
  
  if (nodeText.includes(lowerQuery) || keyText.includes(lowerQuery) || valueText.includes(lowerQuery)) {
    hasMatch = true;
    searchMatchedPaths.value.add(node.path);
  }
  
  // 递归检查子节点
  if (node.children) {
    for (const child of node.children) {
      if (searchInTree(child, query)) {
        hasMatch = true;
        // 如果子节点有匹配，标记当前节点路径为需要展开
        searchMatchedPaths.value.add(node.path);
      }
    }
  }
  
  return hasMatch;
}

// 执行搜索
function performSearch() {
  searchMatchedPaths.value.clear();
  if (currentTab.value && currentTab.value.jsonTree && currentTab.value.searchQuery.trim()) {
    searchInTree(currentTab.value.jsonTree, currentTab.value.searchQuery);
  }
}

// 监听当前tab变化，重新执行搜索
watch(currentTab, () => {
  performSearch();
}, { deep: true });

// 转义正则表达式特殊字符
function escapeRegExp(string: string): string {
  return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
}

// 格式化JSON
function formatJson(): void {
  if (!currentTab.value) return;
  
  try {
    if (!currentTab.value.jsonInput.trim()) {
      currentTab.value.errorMessage = "请输入JSON内容";
      return;
    }
    
    const parsed = JSON.parse(currentTab.value.jsonInput);
    currentTab.value.formattedJson = JSON.stringify(parsed, null, 2);
    currentTab.value.compressedJson = JSON.stringify(parsed);
    currentTab.value.jsonString = convertToJsonString(parsed);
    currentTab.value.jsonTree = buildJsonTree(parsed);
    currentTab.value.errorMessage = "";
    currentTab.value.activeViewTab = "formatted";
    // 执行搜索以更新匹配路径
    performSearch();
  } catch (error) {
    console.error('JSON解析错误:', error);
    currentTab.value.errorMessage = `JSON格式错误: ${error instanceof Error ? error.message : String(error)}`;
    currentTab.value.formattedJson = "";
    currentTab.value.compressedJson = "";
    currentTab.value.jsonString = "";
    currentTab.value.jsonTree = null;
  }
}

// 压缩JSON
function compressJson(): void {
  if (!currentTab.value) return;
  
  try {
    if (!currentTab.value.jsonInput.trim()) {
      currentTab.value.errorMessage = "请输入JSON内容";
      return;
    }
    
    const parsed = JSON.parse(currentTab.value.jsonInput);
    currentTab.value.compressedJson = JSON.stringify(parsed);
    currentTab.value.formattedJson = JSON.stringify(parsed, null, 2);
    currentTab.value.jsonString = convertToJsonString(parsed);
    currentTab.value.jsonTree = buildJsonTree(parsed);
    currentTab.value.errorMessage = "";
    currentTab.value.activeViewTab = "compressed";
    // 执行搜索以更新匹配路径
    performSearch();
  } catch (error) {
    console.error('JSON解析错误:', error);
    currentTab.value.errorMessage = `JSON格式错误: ${error instanceof Error ? error.message : String(error)}`;
  }
}

// 清空JSON
function clearJson(): void {
  if (!currentTab.value) return;
  
  currentTab.value.jsonInput = "";
  currentTab.value.formattedJson = "";
  currentTab.value.compressedJson = "";
  currentTab.value.jsonString = "";
  currentTab.value.jsonTree = null;
  currentTab.value.selectedNode = null;
  currentTab.value.showDetails = false;
  currentTab.value.searchQuery = "";
  currentTab.value.errorMessage = "";
}

// 读取JSON文件
async function readJsonFile(): Promise<void> {
  try {
    const selected = await open({
      multiple: false,
      filters: [{
        name: 'JSON',
        extensions: ['json']
      }]
    });
    
    if (selected && currentTab.value) {
      const contents = await readTextFile(selected as string);
      currentTab.value.jsonInput = contents;
      formatJson(); // 自动格式化读取的文件
    }
  } catch (error) {
    console.error('读取文件错误:', error);
    if (currentTab.value) {
      currentTab.value.errorMessage = `读取文件失败: ${error instanceof Error ? error.message : String(error)}`;
    }
  }
}

// 转为JSON字符串
function convertToJsonString(obj: any): string {
  try {
    // 将对象转换为字符串形式，处理特殊字符和转义
    let result = JSON.stringify(obj)
      .replace(/\\/g, '\\\\')
      .replace(/"/g, '\\"');
    
    // 添加双引号，使其成为有效的字符串字面量
    return `"${result}"`;
  } catch (error) {
    console.error('转换为JSON字符串错误:', error);
    if (currentTab.value) {
      currentTab.value.errorMessage = `转换为JSON字符串失败: ${error instanceof Error ? error.message : String(error)}`;
    }
    return "";
  }
}

// 转为JSON字符串
function toJsonString(): void {
  if (!currentTab.value) return;
  
  try {
    if (!currentTab.value.jsonInput.trim()) {
      currentTab.value.errorMessage = "请输入JSON内容";
      return;
    }
    
    const parsed = JSON.parse(currentTab.value.jsonInput);
    currentTab.value.jsonString = convertToJsonString(parsed);
    currentTab.value.formattedJson = JSON.stringify(parsed, null, 2);
    currentTab.value.compressedJson = JSON.stringify(parsed);
    currentTab.value.jsonTree = buildJsonTree(parsed);
    currentTab.value.errorMessage = "";
    currentTab.value.activeViewTab = "string";
  } catch (error) {
    console.error('JSON解析错误:', error);
    currentTab.value.errorMessage = `JSON格式错误: ${error instanceof Error ? error.message : String(error)}`;
  }
}

// 添加转义
function addEscape(): void {
  if (!currentTab.value) return;
  
  if (!currentTab.value.jsonInput.trim()) {
    currentTab.value.errorMessage = "请输入JSON内容";
    return;
  }
  
  try {
    // 将双引号转义为 \"
    const escapedJson = currentTab.value.jsonInput.replace(/"/g, '\\"');
    
    currentTab.value.jsonInput = escapedJson;
    currentTab.value.errorMessage = "";
    showEscapeDropdown.value = false;
  } catch (error) {
    console.error('添加转义错误:', error);
    currentTab.value.errorMessage = `添加转义失败: ${error instanceof Error ? error.message : String(error)}`;
  }
}

// 移除转义
function removeEscape(): void {
  if (!currentTab.value) return;
  
  if (!currentTab.value.jsonInput.trim()) {
    currentTab.value.errorMessage = "请输入JSON内容";
    return;
  }
  
  try {
    // 将 \" 转义为双引号
    const unescapedJson = currentTab.value.jsonInput.replace(/\\"/g, '"');
    
    currentTab.value.jsonInput = unescapedJson;
    currentTab.value.errorMessage = "";
    showEscapeDropdown.value = false;
  } catch (error) {
    console.error('移除转义错误:', error);
    currentTab.value.errorMessage = `移除转义失败: ${error instanceof Error ? error.message : String(error)}`;
  }
}

// 切换转义下拉菜单
function toggleEscapeDropdown(): void {
  showEscapeDropdown.value = !showEscapeDropdown.value;
}

// 构建JSON树形结构
function buildJsonTree(obj: any, path: string = "root"): any {
  if (obj === null) {
    return { type: "null", value: null, path, key: getKeyFromPath(path) };
  }
  
  if (typeof obj === "object") {
    if (Array.isArray(obj)) {
      return {
        type: "array",
        length: obj.length,
        path,
        key: getKeyFromPath(path),
        children: obj.map((item, index) => 
          buildJsonTree(item, `${path}[${index}]`)
        )
      };
    } else {
      return {
        type: "object",
        path,
        key: getKeyFromPath(path),
        children: Object.entries(obj).map(([key, value]) => 
          buildJsonTree(value, `${path}.${key}`)
        )
      };
    }
  }
  
  return {
    type: typeof obj,
    value: obj,
    path,
    key: getKeyFromPath(path)
  };
}

// 从路径获取键名
function getKeyFromPath(path: string): string {
  const parts = path.split(/[.\[\]]/).filter(Boolean);
  return parts[parts.length - 1] || "root";
}

// 选择树节点
function selectNode(node: any): void {
  if (!currentTab.value) return;
  currentTab.value.selectedNode = node;
  currentTab.value.showDetails = true;
}

// 处理转换结果
function handleConverted(result: string, format: string): void {
  if (!currentTab.value) return;
  currentTab.value.convertedResult = result;
  currentTab.value.convertedFormat = format;
  currentTab.value.errorMessage = "";
  currentTab.value.activeViewTab = "converted";
}

// 处理转换错误
function handleConvertError(message: string): void {
  if (!currentTab.value) return;
  currentTab.value.errorMessage = message;
}

// 关闭详情面板
function closeDetails(): void {
  if (!currentTab.value) return;
  currentTab.value.showDetails = false;
}

// 复制到剪贴板
async function copyToClipboard(text: string, type: string): Promise<void> {
  try {
    await navigator.clipboard.writeText(text);
    // 可以添加一个临时的成功提示
    console.log(`${type}已复制到剪贴板`);
  } catch (err) {
    console.error('复制失败:', err);
    // 降级方案：使用传统的复制方法
    const textArea = document.createElement('textarea');
    textArea.value = text;
    document.body.appendChild(textArea);
    textArea.select();
    document.execCommand('copy');
    document.body.removeChild(textArea);
    console.log(`${type}已复制到剪贴板（降级方案）`);
  }
}

// 打开JSON对比弹窗
function openJsonCompare() {
  showJsonCompare.value = true;
}

// 关闭JSON对比弹窗
function closeJsonCompare() {
  showJsonCompare.value = false;
}

</script>

<template>
  <div class="app">
    <!-- Tab页管理栏 -->
    <div class="tab-bar">
      <div class="tab-list">
        <div 
          v-for="tab in tabs" 
          :key="tab.id"
          :class="['tab-item', { active: activeTabId === tab.id }]"
          @click="switchTab(tab.id)"
        >
          <span class="tab-title">{{ tab.title }}</span>
          <button 
            v-if="tabs.length > 1"
            @click.stop="closeTab(tab.id)" 
            class="tab-close"
          >
            ×
          </button>
        </div>
        <button @click="addNewTab" class="add-tab-btn">
          + 新建
        </button>
      </div>
    </div>

    <!-- 头部工具栏 -->
    <header class="header">
      <h1 class="title">JSON格式化工具</h1>
      <div class="toolbar">
        <button @click="readJsonFile" class="btn btn-primary">
          📁 读取文件
        </button>
        <button @click="formatJson" class="btn btn-success">
          ✨ 格式化
        </button>
        <button @click="compressJson" class="btn btn-warning">
          🗜️ 压缩
        </button>
        <button @click="toJsonString" class="btn btn-info">
          🔤 转字符串
        </button>
        <!-- 转换工具 -->
        <ConvertTool 
          :json-input="currentTab?.jsonInput || ''" 
          @converted="handleConverted" 
          @error="handleConvertError"
        />
        <!-- 转义下拉按钮 -->
        <div class="dropdown">
          <button @click="toggleEscapeDropdown" class="btn btn-secondary dropdown-toggle">
            🔧 转义
          </button>
          <div v-if="showEscapeDropdown" class="dropdown-menu">
            <button @click="addEscape" class="dropdown-item">
              ➕ 添加转义
            </button>
            <button @click="removeEscape" class="dropdown-item">
              ➖ 移除转义
            </button>
          </div>
        </div>
        <button @click="clearJson" class="btn btn-danger">
          🗑️ 清空
        </button>
      </div>
    </header>

    <!-- 搜索栏 -->
    <div class="search-bar">
      <input 
        v-model="currentTab.searchQuery" 
        placeholder="🔍 搜索JSON内容..." 
        class="search-input"
      />
    </div>

    <!-- 错误信息 -->
    <div v-if="currentTab?.errorMessage" class="error-message">
      ⚠️ {{ currentTab.errorMessage }}
    </div>

    <!-- 主要内容区域 -->
    <div class="main-content">
      <!-- 左侧：JSON输入区域 -->
      <div class="input-section">
        <h3>JSON输入</h3>
        <textarea 
          v-model="currentTab.jsonInput" 
          placeholder="请输入或粘贴JSON内容..." 
          class="json-input"
        ></textarea>
      </div>

      <!-- 中间：输出区域 -->
      <div class="output-section">
        <div class="tabs">
          <button 
            @click="currentTab.activeViewTab = 'formatted'" 
            :class="['tab', { active: currentTab?.activeViewTab === 'formatted' }]"
          >
            📄 格式化
          </button>
          <button 
            @click="currentTab.activeViewTab = 'compressed'" 
            :class="['tab', { active: currentTab?.activeViewTab === 'compressed' }]"
          >
            📦 压缩
          </button>
          <button 
            @click="currentTab.activeViewTab = 'tree'" 
            :class="['tab', { active: currentTab?.activeViewTab === 'tree' }]"
          >
            🌳 树形结构
          </button>
          <button 
            @click="currentTab.activeViewTab = 'string'" 
            :class="['tab', { active: currentTab?.activeViewTab === 'string' }]"
          >
            🔤 字符串
          </button>
          <button 
            @click="currentTab.activeViewTab = 'converted'" 
            :class="['tab', { active: currentTab?.activeViewTab === 'converted' }]"
            v-if="currentTab?.convertedResult"
          >
            🔄 {{ currentTab?.convertedFormat?.toUpperCase() }}
          </button>
        </div>

        <!-- 格式化JSON显示 -->
        <div v-if="currentTab?.activeViewTab === 'formatted'" class="output-content">
          <div v-if="currentTab?.formattedJson" class="output-wrapper">
            <button @click="copyToClipboard(currentTab.formattedJson, '格式化JSON')" class="copy-btn">
              📋 复制
            </button>
            <pre v-html="highlightedJson" class="json-output"></pre>
          </div>
          <div v-else class="placeholder">格式化后的JSON将显示在这里</div>
        </div>

        <!-- 压缩JSON显示 -->
        <div v-if="currentTab?.activeViewTab === 'compressed'" class="output-content">
          <div v-if="currentTab?.compressedJson" class="output-wrapper">
            <button @click="copyToClipboard(currentTab.compressedJson, '压缩JSON')" class="copy-btn">
              📋 复制
            </button>
            <pre class="json-output compressed">{{ currentTab.compressedJson }}</pre>
          </div>
          <div v-else class="placeholder">压缩后的JSON将显示在这里</div>
        </div>

        <!-- 树形结构显示 -->
        <div v-if="currentTab?.activeViewTab === 'tree'" class="output-content">
          <div v-if="currentTab?.jsonTree" class="tree-view">
            <TreeNode 
              :node="currentTab.jsonTree" 
              @select="selectNode" 
              :selected="currentTab?.selectedNode"
              :search-query="currentTab?.searchQuery || ''"
              :search-matched-paths="searchMatchedPaths"
            />
          </div>
          <div v-else class="placeholder">JSON树形结构将显示在这里</div>
        </div>

        <!-- JSON字符串显示 -->
        <div v-if="currentTab?.activeViewTab === 'string'" class="output-content">
          <div v-if="currentTab?.jsonString" class="output-wrapper">
            <button @click="copyToClipboard(currentTab.jsonString, 'JSON字符串')" class="copy-btn">
              📋 复制
            </button>
            <pre class="json-output string">{{ currentTab.jsonString }}</pre>
          </div>
          <div v-else class="placeholder">JSON字符串将显示在这里</div>
        </div>

        <!-- 转换结果显示 -->
        <div v-if="currentTab?.activeViewTab === 'converted'" class="output-content">
          <div v-if="currentTab?.convertedResult" class="output-wrapper">
            <button @click="copyToClipboard(currentTab.convertedResult, '转换结果')" class="copy-btn">
              📋 复制
            </button>
            <pre class="json-output converted">{{ currentTab.convertedResult }}</pre>
          </div>
          <div v-else class="placeholder">转换结果将显示在这里</div>
        </div>
      </div>

      <!-- 右侧：详情面板 -->
      <div v-if="currentTab?.showDetails && currentTab?.selectedNode" class="details-section">
        <h3>节点详情</h3>
        <div class="details-content">
          <div class="detail-item">
            <strong>路径:</strong> {{ currentTab.selectedNode.path }}
          </div>
          <div class="detail-item">
            <strong>类型:</strong> {{ currentTab.selectedNode.type }}
          </div>
          <div v-if="currentTab.selectedNode.type === 'array'" class="detail-item">
            <strong>长度:</strong> {{ currentTab.selectedNode.length }}
          </div>
          <div v-if="currentTab.selectedNode.type === 'object'" class="detail-item">
            <strong>属性数量:</strong> {{ currentTab.selectedNode.children?.length || 0 }}
          </div>
          <div v-if="currentTab.selectedNode.value !== undefined" class="detail-item">
            <strong>值:</strong>
            <pre class="detail-value">{{ JSON.stringify(currentTab.selectedNode.value, null, 2) }}</pre>
          </div>
        </div>
        <button @click="closeDetails" class="btn btn-secondary close-details">
          ✕ 关闭
        </button>
      </div>
    </div>

    <!-- JSON对比弹窗 -->
    <JsonCompare 
      :visible="showJsonCompare" 
      @close="closeJsonCompare"
    />
  </div>
</template>



<style>
/* 全局样式 */
:root {
  font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
  font-size: 14px;
  line-height: 1.5;
  color: #333;
  background-color: #f5f5f5;
}

* {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
}

/* 应用主容器 */
.app {
  height: 100vh;
  display: flex;
  flex-direction: column;
  background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
  overflow: visible;
}

/* 头部工具栏 */
.header {
  background: rgba(255, 255, 255, 0.95);
  backdrop-filter: blur(10px);
  padding: 1rem 2rem;
  display: flex;
  justify-content: space-between;
  align-items: center;
  box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
  border-bottom: 1px solid rgba(255, 255, 255, 0.2);
  overflow: visible;
  position: relative;
  z-index: 10;
}

.title {
  font-size: 1.5rem;
  font-weight: 600;
  color: #2c3e50;
  margin: 0;
}

.toolbar {
  display: flex;
  gap: 0.5rem;
  overflow: visible;
}

/* 按钮样式 */
.btn {
  padding: 0.5rem 1rem;
  border: none;
  border-radius: 6px;
  font-size: 0.9rem;
  font-weight: 500;
  cursor: pointer;
  transition: all 0.2s ease;
  display: flex;
  align-items: center;
  gap: 0.3rem;
}

.btn:hover {
  transform: translateY(-1px);
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
}

.btn-primary {
  background: #3498db;
  color: white;
}

.btn-primary:hover {
  background: #2980b9;
}

.btn-success {
  background: #2ecc71;
  color: white;
}

.btn-success:hover {
  background: #27ae60;
}

.btn-warning {
  background: #f39c12;
  color: white;
}

.btn-warning:hover {
  background: #e67e22;
}

.btn-danger {
  background: #e74c3c;
  color: white;
}

.btn-danger:hover {
  background: #c0392b;
}

.btn-secondary {
  background: #95a5a6;
  color: white;
}

.btn-secondary:hover {
  background: #7f8c8d;
}

.btn-info {
  background: #9b59b6;
  color: white;
}

.btn-info:hover {
  background: #8e44ad;
}

/* 搜索栏 */
.search-bar {
  padding: 1rem 2rem;
  background: rgba(255, 255, 255, 0.9);
  backdrop-filter: blur(5px);
  position: relative;
  z-index: 1;
}

.search-input {
  width: 100%;
  padding: 0.75rem 1rem;
  border: 2px solid #ddd;
  border-radius: 8px;
  font-size: 1rem;
  transition: border-color 0.2s ease;
}

.search-input:focus {
  outline: none;
  border-color: #3498db;
  box-shadow: 0 0 0 3px rgba(52, 152, 219, 0.1);
}

/* 错误信息 */
.error-message {
  background: #e74c3c;
  color: white;
  padding: 0.75rem 2rem;
  font-weight: 500;
  animation: slideDown 0.3s ease;
}

@keyframes slideDown {
  from {
    transform: translateY(-100%);
    opacity: 0;
  }
  to {
    transform: translateY(0);
    opacity: 1;
  }
}

/* 主要内容区域 */
.main-content {
  flex: 1;
  display: grid;
  grid-template-columns: 1fr 2fr 300px;
  gap: 1rem;
  padding: 1rem 2rem;
  overflow: hidden;
}

/* 输入区域 */
.input-section {
  background: rgba(255, 255, 255, 0.95);
  border-radius: 12px;
  padding: 1.5rem;
  box-shadow: 0 4px 20px rgba(0, 0, 0, 0.1);
  display: flex;
  flex-direction: column;
}

.input-section h3 {
  margin-bottom: 1rem;
  color: #2c3e50;
  font-weight: 600;
}

.json-input {
  flex: 1;
  border: 2px solid #ddd;
  border-radius: 8px;
  padding: 1rem;
  font-family: 'Consolas', 'Monaco', 'Courier New', monospace;
  font-size: 0.9rem;
  line-height: 1.4;
  resize: none;
  transition: border-color 0.2s ease;
}

.json-input:focus {
  outline: none;
  border-color: #3498db;
  box-shadow: 0 0 0 3px rgba(52, 152, 219, 0.1);
}

/* 输出区域 */
.output-section {
  background: rgba(255, 255, 255, 0.95);
  border-radius: 12px;
  box-shadow: 0 4px 20px rgba(0, 0, 0, 0.1);
  display: flex;
  flex-direction: column;
  overflow: hidden;
}

/* 标签页 */
.tabs {
  display: flex;
  background: #f8f9fa;
  border-bottom: 1px solid #dee2e6;
}

.tab {
  flex: 1;
  padding: 1rem;
  border: none;
  background: transparent;
  cursor: pointer;
  font-weight: 500;
  transition: all 0.2s ease;
  border-bottom: 3px solid transparent;
}

.tab:hover {
  background: rgba(52, 152, 219, 0.1);
}

.tab.active {
  background: white;
  border-bottom-color: #3498db;
  color: #3498db;
}

/* 输出内容 */
.output-content {
  flex: 1;
  padding: 1.5rem;
  overflow: auto;
}

.json-output {
  font-family: 'Consolas', 'Monaco', 'Courier New', monospace;
  font-size: 0.85rem;
  line-height: 1.4;
  background: #f8f9fa;
  padding: 1rem;
  border-radius: 6px;
  border: 1px solid #e9ecef;
  white-space: pre-wrap;
  word-wrap: break-word;
  margin: 0;
}

.json-output.compressed {
  white-space: pre-wrap;
  word-break: break-all;
}

.json-output.converted {
  white-space: pre-wrap;
  word-wrap: break-word;
  background: #f0f8ff;
  border-left: 4px solid #17a2b8;
}

.placeholder {
  text-align: center;
  color: #6c757d;
  font-style: italic;
  padding: 2rem;
}

/* 搜索高亮 */
.json-output mark {
  background: #fff3cd;
  color: #856404;
  padding: 0.1rem 0.2rem;
  border-radius: 3px;
}

/* 树形视图 */
.tree-view {
  font-family: 'Consolas', 'Monaco', 'Courier New', monospace;
  font-size: 0.85rem;
}

.tree-node {
  margin-left: 0;
}

.node-content {
  padding: 0.3rem 0.5rem;
  cursor: pointer;
  border-radius: 4px;
  transition: background-color 0.2s ease;
  display: flex;
  align-items: center;
  gap: 0.5rem;
}

.node-content:hover {
  background: rgba(52, 152, 219, 0.1);
}

.node-content.selected {
  background: #3498db;
  color: white;
}

.node-children {
  margin-left: 1.5rem;
  border-left: 1px dashed #ddd;
  padding-left: 0.5rem;
}

.node-icon {
  font-size: 1rem;
}

.node-text {
  flex: 1;
}

/* 节点类型样式 */
.node-object {
  color: #8e44ad;
}

.node-array {
  color: #e67e22;
}

.node-string {
  color: #27ae60;
}

.node-number {
  color: #3498db;
}

.node-boolean {
  color: #f39c12;
}

.node-null {
  color: #95a5a6;
}

/* 详情面板 */
.details-section {
  background: rgba(255, 255, 255, 0.95);
  border-radius: 12px;
  padding: 1.5rem;
  box-shadow: 0 4px 20px rgba(0, 0, 0, 0.1);
  display: flex;
  flex-direction: column;
  overflow: auto;
}

.details-section h3 {
  margin-bottom: 1rem;
  color: #2c3e50;
  font-weight: 600;
}

.details-content {
  flex: 1;
}

.detail-item {
  margin-bottom: 1rem;
  padding: 0.75rem;
  background: #f8f9fa;
  border-radius: 6px;
  border-left: 4px solid #3498db;
}

/* 下拉菜单样式 */
.dropdown {
  position: relative;
  display: inline-block;
}

.dropdown-toggle {
  position: relative;
}

.dropdown-toggle::after {
  content: '▼';
  font-size: 0.7rem;
  margin-left: 0.5rem;
  opacity: 0.7;
}

.dropdown-menu {
  position: absolute;
  top: 100%;
  left: 0;
  z-index: 99999;
  min-width: 160px;
  background: white;
  border: 1px solid #dee2e6;
  border-radius: 6px;
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
  margin-top: 0.25rem;
  overflow: hidden;
}

.dropdown-item {
  display: block;
  width: 100%;
  padding: 0.75rem 1rem;
  border: none;
  background: transparent;
  text-align: left;
  cursor: pointer;
  font-size: 0.9rem;
  transition: background-color 0.2s ease;
  color: #495057;
}

.dropdown-item:hover {
  background: #f8f9fa;
  color: #16181b;
}

.dropdown-item:active {
  background: #e9ecef;
}

.btn-secondary {
  background: #6c757d;
  border-color: #6c757d;
  color: white;
}

.btn-secondary:hover {
  background: #5a6268;
  border-color: #545b62;
}

.detail-item strong {
  color: #2c3e50;
  display: block;
  margin-bottom: 0.25rem;
}

.detail-value {
  background: #2c3e50;
  color: #ecf0f1;
  padding: 0.75rem;
  border-radius: 4px;
  font-family: 'Consolas', 'Monaco', 'Courier New', monospace;
  font-size: 0.8rem;
  margin-top: 0.5rem;
  white-space: pre-wrap;
  word-wrap: break-word;
}

.close-details {
  margin-top: 1rem;
  align-self: flex-start;
}

/* 响应式设计 */
@media (max-width: 1200px) {
  .main-content {
    grid-template-columns: 1fr 1fr;
  }
  
  .details-section {
    grid-column: 1 / -1;
    max-height: 200px;
  }
}

@media (max-width: 768px) {
  .main-content {
    grid-template-columns: 1fr;
    gap: 0.5rem;
  }
  
  .header {
    flex-direction: column;
    gap: 1rem;
    text-align: center;
  }
  
  .toolbar {
    flex-wrap: wrap;
    justify-content: center;
  }
}

/* 滚动条样式 */
::-webkit-scrollbar {
  width: 8px;
  height: 8px;
}

::-webkit-scrollbar-track {
  background: #f1f1f1;
  border-radius: 4px;
}

::-webkit-scrollbar-thumb {
  background: #c1c1c1;
  border-radius: 4px;
}

::-webkit-scrollbar-thumb:hover {
  background: #a8a8a8;
}

/* Tab页管理栏样式 */
.tab-bar {
  background: rgba(255, 255, 255, 0.9);
  border-bottom: 1px solid #dee2e6;
  padding: 0.5rem 2rem;
  overflow-x: auto;
}

.tab-list {
  display: flex;
  align-items: center;
  gap: 0.25rem;
  min-width: max-content;
}

.tab-item {
  display: flex;
  align-items: center;
  padding: 0.5rem 1rem;
  background: #f8f9fa;
  border: 1px solid #dee2e6;
  border-radius: 6px 6px 0 0;
  cursor: pointer;
  transition: all 0.2s ease;
  max-width: 200px;
  min-width: 120px;
}

.tab-item:hover {
  background: #e9ecef;
}

.tab-item.active {
  background: white;
  border-bottom-color: white;
  box-shadow: 0 -2px 4px rgba(0, 0, 0, 0.1);
}

.tab-title {
  flex: 1;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  font-size: 0.9rem;
  font-weight: 500;
}

.tab-close {
  background: none;
  border: none;
  font-size: 1.2rem;
  color: #6c757d;
  cursor: pointer;
  padding: 0;
  margin-left: 0.5rem;
  width: 20px;
  height: 20px;
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: 50%;
  transition: all 0.2s ease;
}

.tab-close:hover {
  background: #dc3545;
  color: white;
}

.add-tab-btn {
  background: #28a745;
  color: white;
  border: none;
  padding: 0.5rem 1rem;
  border-radius: 6px;
  cursor: pointer;
  font-size: 0.9rem;
  font-weight: 500;
  transition: all 0.2s ease;
  white-space: nowrap;
}

.add-tab-btn:hover {
  background: #218838;
  transform: translateY(-1px);
}

/* 输出包装器和复制按钮样式 */
.output-wrapper {
  position: relative;
}

.copy-btn {
  position: absolute;
  top: 0.5rem;
  right: 0.5rem;
  background: #17a2b8;
  color: white;
  border: none;
  padding: 0.25rem 0.5rem;
  border-radius: 4px;
  font-size: 0.8rem;
  cursor: pointer;
  z-index: 10;
  transition: all 0.2s ease;
  opacity: 0.8;
}

.copy-btn:hover {
  background: #138496;
  opacity: 1;
  transform: translateY(-1px);
}
</style>